package com.atguigu.tingshu.search.service.impl;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Hit;
import com.alibaba.fastjson.JSONObject;
import com.atguigu.tingshu.album.client.AlbumInfoFeignClient;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.util.PinYinUtils;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.BaseCategoryView;
import com.atguigu.tingshu.model.search.AlbumInfoIndex;
import com.atguigu.tingshu.model.search.AttributeValueIndex;
import com.atguigu.tingshu.model.search.SuggestIndex;
import com.atguigu.tingshu.search.executor.ExpireThreadExecutor;
import com.atguigu.tingshu.search.factory.ScheduleTaskThreadPoolFactory;
import com.atguigu.tingshu.search.repository.AlbumInfoIndexRepository;
import com.atguigu.tingshu.search.repository.SuggestIndexRepository;
import com.atguigu.tingshu.search.runnble.RebuildBloomFilterRunnable;
import com.atguigu.tingshu.search.service.ItemService;
import com.atguigu.tingshu.user.client.UserInfoFeignClient;
import com.atguigu.tingshu.vo.album.AlbumStatVo;
import com.atguigu.tingshu.vo.search.AlbumInfoIndexVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.google.common.collect.Lists;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.suggest.Completion;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.concurrent.*;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class ItemServiceImpl implements ItemService {

    @Autowired
    private AlbumInfoIndexRepository albumInfoIndexRepository;

    @Autowired
    private SuggestIndexRepository suggestIndexRepository;

    @Resource
    private AlbumInfoFeignClient albumInfoFeignClient;

    @Resource
    private UserInfoFeignClient userInfoFeignClient;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private ElasticsearchClient elasticsearchClient;

    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;

    @Autowired
    private RBloomFilter rBloomFilter;

    ExecutorService executorService = Executors.newFixedThreadPool(4);

    Map<Long, Map<String, Object>> localCache = new ConcurrentHashMap<>();    //本地map缓存

    ThreadLocal<String> reentrantLockTokenThreadLocal = new ThreadLocal<>();

    @Autowired
    private ItemServiceImpl itemServiceImpl;

    @Autowired
    private RedissonClient redissonClient;

    /**
     * @description:使用定时任务线程池重建布隆过滤器
     * @author: baosi
     * @date: 2025/3/27 11:09
     */
    @PostConstruct // spring在创建ItemServiceImpl Bean对象的时候，在生命周期走到初始化前这个阶段就会来回调该方法
    public void initRebuildBloomFilter() {
        // 从当前开始算7天之后的凌晨两点执行第一次。--线上使用的
        ScheduleTaskThreadPoolFactory instance = ScheduleTaskThreadPoolFactory.getINSTANCE();
        Long taskFirstTime = instance.diffTime(System.currentTimeMillis());
        instance.execute(new RebuildBloomFilterRunnable(redissonClient, redisTemplate, itemServiceImpl), taskFirstTime, TimeUnit.MILLISECONDS);
    }

    @Override
    public Map<String, Object> getAlbumInfo(Long albumId) {
//        String cacheKey = RedisConstant.CACHE_INFO_PREFIX + albumId;
//        String lockKey = RedisConstant.ALBUM_LOCK_SUFFIX + albumId;
//        String token = "";
//        Boolean acquireLockFlag = false;
//
//        //查询本地缓存
//        if (localCache.containsKey(albumId)) {
//            return localCache.get(albumId);
//        }
//
//        //查询分布式缓存
//        String resultFromCache = redisTemplate.opsForValue().get(cacheKey);
//        if (StringUtils.hasText(resultFromCache)) {
//            Map result = JSONObject.parseObject(resultFromCache, Map.class);
//            localCache.put(albumId, result);
//            return result;
//        }
//
//        //未命中查询数据库
//        //获取token 解决可重入
//        String s = reentrantLockTokenThreadLocal.get();
//        //是递归进来的县城
//        if (StringUtils.hasText(s)) {
//            token = s;
//            acquireLockFlag = true;
//        } else {
//            //第一次进来
//            token = UUID.randomUUID().toString().replace("-", "");
//            acquireLockFlag = redisTemplate.opsForValue().setIfAbsent(lockKey, token, 30, TimeUnit.SECONDS);//  服务端给锁一个过期时间  避免死锁发生
//        }
//        //加锁成功
//        if (Boolean.TRUE.equals(acquireLockFlag)) {
//            //续期
//            ExpireThreadExecutor expireThreadExecutor = new ExpireThreadExecutor(redisTemplate, albumId);
//            expireThreadExecutor.renewal(30L, TimeUnit.SECONDS);
//            Map<String, Object> albumInfoFromDb;
//            try {
//                // 查询数据库
//                albumInfoFromDb = getAlbumInfoFromDb(albumId);
//                // 将数据库查询的数据同步给缓存Redis
//                redisTemplate.opsForValue().set(cacheKey, JSONObject.toJSONString(albumInfoFromDb));
//            } finally {
//                //释放锁
//                //是自己的锁删除 否则不删
//                String luaScript = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
//                Long execute = redisTemplate.execute(new DefaultRedisScript<Long>(luaScript, Long.class), List.of(lockKey), token);
//                Assert.notNull(execute, "redis释放锁异常");
//                if (execute == 0) {
//                    log.error("释放锁失败");
//                } else {
//                    log.info("释放锁成功");
//                }
//                //移除token 防止内存泄漏
//                reentrantLockTokenThreadLocal.remove();
//                //结束续期
//                expireThreadExecutor.cancelRenewal();
//            }
//            localCache.put(albumId, albumInfoFromDb);
//            return albumInfoFromDb;
//        } else {
//            //加锁失败
//            try {
//                Thread.sleep(200);// 等待同步时间
//            } catch (InterruptedException e) {
//                throw new RuntimeException(e);
//            }
//            //查询缓存
//            String firstCacheStr = redisTemplate.opsForValue().get(cacheKey);
//            if (StringUtils.hasText(firstCacheStr)) {
//                Map result = JSONObject.parseObject(firstCacheStr, Map.class);
//                localCache.put(albumId, result);
//                return result;
//            }
//
//            //少数情况,得到锁的线程异常 没有正确把结果存到redis
//            while (true) {
//                // 查询缓存的作用：主要是为了解决，递归进去的线程将数据同步到缓存之后，其它线程还要抢锁。
//                String doubleCacheStr = redisTemplate.opsForValue().get(cacheKey);
//                // 如果有，要在while..true抢锁的线程不用在抢锁。直接将递归进去的线程放到缓存中的数据返回即可。
//                if (StringUtils.hasText(doubleCacheStr)) {
//                    return JSONObject.parseObject(doubleCacheStr);
//                }
//                //  抢锁
//                Boolean acquireLock = redisTemplate.opsForValue().setIfAbsent(lockKey, token, 30, TimeUnit.SECONDS);
//                if (Boolean.TRUE.equals(acquireLock)) {
//                    reentrantLockTokenThreadLocal.set(token); //将该线程加锁的令牌存放到ThrealLocal.主要解决递归进去的线程不在加锁。【保证可重入锁】
//                    break; // 退出循环
//                }
//            }
//            //重新递归查询数据库
//            return getAlbumInfo(albumId);
        return getDistroCacheAndLockFinallyRedissonVersion(albumId);
    }

    /**
     * @description: Redission+分布式布隆过滤器
     * @author: baosi
     * @date: 2025/3/27 10:59
     */
    @SneakyThrows
    private Map<String, Object> getDistroCacheAndLockFinallyRedissonVersion(Long albumId) {
        // 1.定义缓存key
        String cacheKey = RedisConstant.CACHE_INFO_PREFIX + albumId;
        String lockKey = RedisConstant.ALBUM_LOCK_SUFFIX + albumId;
        long ttl = 0L;

        // 2.查询分布式布隆过滤器
        boolean contains = rBloomFilter.contains(albumId);
        if (!contains) {
            return null;
        }
        // 3.查询缓存
        String jsonStrFromRedis = redisTemplate.opsForValue().get(cacheKey);

        // 3.1 缓存命中
        if (!StringUtils.isEmpty(jsonStrFromRedis)) {
            return JSONObject.parseObject(jsonStrFromRedis, Map.class);
        }
        // 3.2 缓存未命中 查询数据库
        // 3.2.1 添加分布式锁
        RLock lock = redissonClient.getLock(lockKey);
        boolean accquireLockFlag = lock.tryLock();// 非阻塞 还能续期
        if (accquireLockFlag) {
            try {
                // 3.2.2 回源查询数据
                Map<String, Object> albumInfoFromDb = getAlbumInfoFromDb(albumId);
                if (albumInfoFromDb != null) {
                    ttl = 60 * 60 * 24 * 7L;
                } else {
                    ttl = 60 * 60 * 24L;
                }
                // 3.2.3 同步数据到缓存中去
                redisTemplate.opsForValue().set(cacheKey, JSONObject.toJSONString(albumInfoFromDb), ttl, TimeUnit.SECONDS);// 防止缓存穿透的固定值攻击
                return albumInfoFromDb;
            } finally {
                lock.unlock();// 释放锁
            }

        } else {
            // 等同步时间之后 查询缓存即可。(面试时说加)
            Thread.sleep(200);
            String result = redisTemplate.opsForValue().get(cacheKey);
            if (!StringUtils.isEmpty(result)) {
                return JSONObject.parseObject(result, Map.class);
            }
            return getAlbumInfoFromDb(albumId);
        }
    }

    @Override
    public List<Long> getAlbumInfoIdList() {
        Result<List<Long>> albumIds = albumInfoFeignClient.getAlbumInfoIdList();
        List<Long> albumIdsData = albumIds.getData();
        if (CollectionUtils.isEmpty(albumIdsData)) {
            throw new GuiguException(201, "应用中布存在专辑id集合");
        }
        return albumIdsData;
    }

    @Override
    public Boolean rebuildBloomFilter() {
        // 做法：
        // 1.删除老布隆的数据
        // 2.删除老布隆的配置
        // 3.创建新布隆
        // 4.初始化新布隆
        // 5.将数据放到新布隆
        // 6.新布隆上线就可以使用

        // 优化做法：(高速路上换轮胎)
        // 1.创建新布隆
        // 2.初始化新布隆
        // 3.将数据放到新布隆

        // 4.删除老布隆的数据
        // 5.删除老布隆的配置
        // 6.重命名；老布隆的名字换新布隆的名字---4 5 6做成一个原子操作

        //  新布隆上线就可以使用  rename:重新命令  albumInfoBloomFilterNew   albumInfoBloomFilter
        RBloomFilter<Object> albumIdBloomFilterNew = redissonClient.getBloomFilter("albumIdBloomFilterNew");

        albumIdBloomFilterNew.tryInit(1000000l, 0.001);

        List<Long> albumInfoIdList = getAlbumInfoIdList();   // 重数据中行查询
        for (Long albumId : albumInfoIdList) {
            albumIdBloomFilterNew.add(albumId);
        }
        albumIdBloomFilterNew.add(2000L);// 测试的时候手动添加的。

        // rename key  key1  用key1的名字换key的名字（反向操作）

        String script = " redis.call(\"del\",KEYS[1])" +
                "  redis.call(\"del\",KEYS[2])" +
                "  redis.call(\"rename\",KEYS[3],KEYS[1])" +
                "  redis.call(\"rename\",KEYS[4],KEYS[2]) return 0";
        List<String> asList = Arrays.asList("albumIdBloomFilter", "{albumIdBloomFilter}:config", "albumIdBloomFilterNew", "{albumIdBloomFilterNew}:config");
        Long execute = redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), asList);
        if (execute == 0) {
            log.info("老布隆已经被删除，新布隆上线...");
        }
        return execute == 0;

    }

    @NotNull
    private Map<String, Object> getAlbumInfoFromDb(Long albumId) {
        // 1.创建Map对象
        Map<String, Object> map = new HashMap<>();


        CompletableFuture<Void> albumStatCompletableFuture = CompletableFuture.runAsync(new Runnable() {
            @Override
            public void run() {
                System.out.println("执行查询专辑统计任务用的线程：" + Thread.currentThread().getName());
                // 1. 专辑的统计信息
                Result<AlbumStatVo> albumStatResult = albumInfoFeignClient.getAlbumStat(albumId);
                AlbumStatVo albumStatVoData = albumStatResult.getData();
                if (albumStatVoData == null) {
                    throw new GuiguException(201, "远程查询专辑微服务获取专辑统计信息失败");
                }
                map.put("albumStatVo", albumStatVoData);
            }
        }, threadPoolExecutor);

        CompletableFuture<Void> viewCompletableFuture = CompletableFuture.runAsync(new Runnable() {
            @Override
            public void run() {
                System.out.println("执行查询专辑分类任务用的线程：" + Thread.currentThread().getName());
                // 2. 专辑的分类（分类的名字）`
                Result<BaseCategoryView> albumCategoryResult = albumInfoFeignClient.getAlbumCategory(albumId);
                BaseCategoryView baseCategoryViewData = albumCategoryResult.getData();
                if (baseCategoryViewData == null) {
                    throw new GuiguException(201, "远程查询专辑微服务获取专辑分类信息失败");
                }
                map.put("baseCategoryView", baseCategoryViewData);
            }
        }, threadPoolExecutor);


        CompletableFuture<Long> albumInfoCompletableFuture = CompletableFuture.supplyAsync(new Supplier<Long>() {
            @Override
            public Long get() {
                System.out.println("执行查询专辑基本信息任务用的线程：" + Thread.currentThread().getName());
                // 3. 专辑基本数据
                Result<AlbumInfo> albumInfoAndAttrValueResult = albumInfoFeignClient.getAlbumInfoAndAttrValue(albumId);
                AlbumInfo albumInfoData = albumInfoAndAttrValueResult.getData();
                if (albumInfoData == null) {
                    throw new GuiguException(201, "远程查询专辑微服务获取专辑基本信息失败");
                }
                map.put("albumInfo", albumInfoData);

                return albumInfoData.getUserId();
            }
        }, threadPoolExecutor);

        CompletableFuture<Void> userCompletableFuture = albumInfoCompletableFuture.thenAcceptAsync(new Consumer<Long>() {
            @Override
            public void accept(Long userId) {
                System.out.println("执行查询专辑对应主播任务用的线程：" + Thread.currentThread().getName());
                // 4.查询专辑对应的主播信息
                Result<UserInfoVo> userInfoResult = userInfoFeignClient.getUserInfo(userId);
                UserInfoVo userInfoResultData = userInfoResult.getData();
                if (userInfoResultData == null) {
                    throw new GuiguException(201, "远程查询专辑微服务获取专辑基本信息失败");
                }
                map.put("announcer", userInfoResultData);

            }
        }, threadPoolExecutor);

        CompletableFuture.allOf(albumStatCompletableFuture, viewCompletableFuture, albumInfoCompletableFuture, userCompletableFuture).join();

        return map;
    }

    @SneakyThrows
    @Override
    public void albumOnSale(Long albumId) {

        CountDownLatch countDownLatch = new CountDownLatch(4);  // 其它线程干活的线程数

        // 1.创建文档对象
        AlbumInfoIndex albumInfoIndex = new AlbumInfoIndex();

        Long startTime = System.currentTimeMillis();

        Future<Long> future = executorService.submit(new Callable<Long>() {
            @Override
            public Long call() throws Exception {
                // 2.1 远程查询专辑基本信息
                Result<AlbumInfo> albumInfoResult = albumInfoFeignClient.getAlbumInfoAndAttrValue(albumId);
                AlbumInfo albumInfoData = albumInfoResult.getData();
                if (albumInfoData == null) {
                    throw new GuiguException(201, "远程调用专辑微服务获取专辑信息失败");
                }

                albumInfoIndex.setId(albumInfoData.getId());  // 专辑id
                albumInfoIndex.setAlbumTitle(albumInfoData.getAlbumTitle());   // 专辑标题
                albumInfoIndex.setAlbumIntro(albumInfoData.getAlbumIntro()); // 专辑简介
                albumInfoIndex.setCoverUrl(albumInfoData.getCoverUrl());  // 专辑封面
                albumInfoIndex.setIncludeTrackCount(albumInfoData.getIncludeTrackCount()); // 专辑包含的声音集数
                albumInfoIndex.setIsFinished(albumInfoData.getIsFinished().toString()); // 专辑是否完结
                albumInfoIndex.setPayType(albumInfoData.getPayType()); // 专辑付费类型（免费 vip免费  付费）
                albumInfoIndex.setCreateTime(new Date());  // 专辑保存到es的时间

                List<AttributeValueIndex> attributeValueIndexs = albumInfoData.getAlbumAttributeValueVoList().stream().map(albumAttributeValue -> {
                    AttributeValueIndex attributeValueIndex = new AttributeValueIndex();
                    attributeValueIndex.setAttributeId(albumAttributeValue.getAttributeId());
                    attributeValueIndex.setValueId(albumAttributeValue.getValueId());
                    return attributeValueIndex;
                }).collect(Collectors.toList());
                albumInfoIndex.setAttributeValueIndexList(Lists.newArrayList(attributeValueIndexs));   // 专辑的标签

                countDownLatch.countDown();
                return albumInfoData.getUserId();
            }
        });

        executorService.execute(new Runnable() {

            // 2.2 远程查询主播信息
            @Override
            public void run() {
                Long userId = null;
                try {
                    userId = future.get();   // 当前线程会阻塞
                    Result<UserInfoVo> albumInfoVoResult = userInfoFeignClient.getUserInfo(userId);
                    UserInfoVo userInfoVoData = albumInfoVoResult.getData();
                    Assert.notNull(userInfoVoData, "远程调用用户微服务获取用户信息失败");
                    albumInfoIndex.setAnnouncerName(userInfoVoData.getNickname()); // 专辑对应的主播名字
                } catch (Exception e) {
                    throw new RuntimeException(e);
                } finally {
                    countDownLatch.countDown();
                }

            }
        });


        executorService.execute(new Runnable() {
            // 2.3 远程查询分类信息
            @Override
            public void run() {
                Result<BaseCategoryView> baseCategoryViewResult = albumInfoFeignClient.getAlbumCategory(albumId);
                BaseCategoryView baseCategoryViewData = baseCategoryViewResult.getData();
                Assert.notNull(baseCategoryViewData, "远程调用专辑微服务获取分类信息失败");

                albumInfoIndex.setCategory1Id(baseCategoryViewData.getCategory1Id()); // 专辑一级分类id
                albumInfoIndex.setCategory2Id(baseCategoryViewData.getCategory2Id()); // 专辑二级分类id
                albumInfoIndex.setCategory3Id(baseCategoryViewData.getCategory3Id()); // 专辑二级分类id
                countDownLatch.countDown();
            }
        });


        executorService.execute(new Runnable() {
            // 2.4 远程查询统计信息
            @Override
            public void run() {
                Result<AlbumStatVo> albumStatVoResult = albumInfoFeignClient.getAlbumStat(albumId);
                AlbumStatVo albumStatVoData = albumStatVoResult.getData();
                if (albumStatVoData == null) {
                    throw new GuiguException(201, "远程调用专辑微服务获取专辑分类信息失败");
                }

                Integer commentStatNum = albumStatVoData.getCommentStatNum();
                Integer subscribeStatNum = albumStatVoData.getSubscribeStatNum();
                Integer playStatNum = albumStatVoData.getPlayStatNum();
                Integer buyStatNum = albumStatVoData.getBuyStatNum();

                albumInfoIndex.setPlayStatNum(playStatNum);  // 专辑的播放量
                albumInfoIndex.setSubscribeStatNum(subscribeStatNum); // 专辑的订阅量
                albumInfoIndex.setBuyStatNum(buyStatNum); // 专辑的购买量
                albumInfoIndex.setCommentStatNum(commentStatNum); // 专辑的评论数
                Double hotScore = new Random().nextDouble(); // 测试环境用
                albumInfoIndex.setHotScore(hotScore); // 专辑热度值
                countDownLatch.countDown();
            }
        });

        countDownLatch.await();
        // 3.将文档对象存储到es中
        Long endTime = System.currentTimeMillis();

        log.info("专辑:{}上架到es耗时：{}ms", albumId, endTime - startTime);
        albumInfoIndexRepository.save(albumInfoIndex);

        //保存数据到索引库中
        SuggestIndex suggestIndex = new SuggestIndex();
        suggestIndex.setId(albumInfoIndex.getId().toString());
        suggestIndex.setTitle(albumInfoIndex.getAlbumTitle());

        //关键字
        suggestIndex.setKeyword(
                new Completion(
                        new String[]{
                                albumInfoIndex.getAlbumTitle()
                        }));
        //拼音
        suggestIndex.setKeywordPinyin(
                new Completion(
                        new String[]{
                                PinYinUtils.toHanyuPinyin(
                                        albumInfoIndex.getAlbumTitle()
                                )
                        }));
        //拼音首字母
        suggestIndex.setKeywordSequence(
                new Completion(
                        new String[]{
                                PinYinUtils.getFirstLetter(albumInfoIndex.getAlbumTitle()
                                )
                        }));
        suggestIndexRepository.save(suggestIndex);
    }

    @Override
    public void albumOffSale(Long albumId) {

        try {
            albumInfoIndexRepository.deleteById(albumId);
            log.info("专辑下架成功");
        } catch (Exception e) {
            log.error("专辑下架失败");
        }
    }

    @Override
    public void batchAlbumOffSale() {
        try {
            albumInfoIndexRepository.deleteAll();
            log.info("专辑批量下架成功");
        } catch (Exception e) {
            log.error("专辑批量下架失败");
        }

    }

    @SneakyThrows
    @Override
    public void preRankingToCache() {
        Result<List<Long>> c1IdsResult = albumInfoFeignClient.getAllCategory1Id();
        List<Long> c1IdData = c1IdsResult.getData();
        if (CollectionUtils.isEmpty(c1IdData)) {
            throw new GuiguException(201, "远程查询专辑微服务获取一级分类id失败");
        }

        for (Long c1Id : c1IdData) {

            String[] fiveDimension = {"hotScore", "playStatNum", "subscribeStatNum", "buyStatNum", "commentStatNum"};
            for (String dimension : fiveDimension) {
                SearchResponse<AlbumInfoIndex> response = elasticsearchClient.search(srb -> srb
                        .index("albuminfo")
                        .query(qb -> qb
                                .term(tqb -> tqb
                                        .field("category1Id")
                                        .value(c1Id)))
                        .sort(sob -> sob.field(fsb -> fsb.field(dimension)))
                        .size(10), AlbumInfoIndex.class);

                List<AlbumInfoIndex> albumInfoIndices = new ArrayList<>();

                for (Hit<AlbumInfoIndex> hit : response.hits().hits()) {
                    AlbumInfoIndex albumInfoIndex = hit.source();
                    albumInfoIndices.add(albumInfoIndex);
                }

                // Redis：String  set  zset  hash(大key  小key )  list

                String bigKey = RedisConstant.RANKING_KEY_PREFIX + c1Id;
                redisTemplate.opsForHash().put(bigKey, dimension, JSONObject.toJSONString(albumInfoIndices));
            }
        }
    }

    @Override
    public List<AlbumInfoIndexVo> findRankingList(Long c1Id, String dimension) {
        String bigKey = RedisConstant.RANKING_KEY_PREFIX + c1Id;
        String albumInfoIndexList = (String) redisTemplate.opsForHash().get(bigKey, dimension);
        if (StringUtils.isEmpty(albumInfoIndexList)) {
            throw new GuiguException(201, "排行榜信息不存在");
        }

        List<AlbumInfoIndex> albumInfoIndices = JSONObject.parseArray(albumInfoIndexList, AlbumInfoIndex.class);

        List<AlbumInfoIndexVo> albumInfoIndexVoList = albumInfoIndices.stream().map(albumInfoIndex -> {
            AlbumInfoIndexVo albumInfoIndexVo = new AlbumInfoIndexVo();
            BeanUtils.copyProperties(albumInfoIndex, albumInfoIndexVo);
            return albumInfoIndexVo;
        }).collect(Collectors.toList());

        return albumInfoIndexVoList;
    }

}
